package com.firstsight.micro.homedo.service.impl;


import com.firstsight.micro.homedo.entity.TaskRunnable;
import com.firstsight.micro.homedo.service.ILockerService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class LockerServiceImpl implements ILockerService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static String LOCKER_KEY = "locker:key";
    private static String INVENTORY = "inventory";
    private Map<String, ScheduledFuture<?>> taskMap = new HashMap<>();

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * synchronized JVM级别的锁，在同一服务容器里面生效，但是微服务模式下 不容乐观
     */
    @Override
    public void synchronizedTest() {
        synchronized (this) {
            String num = redisTemplate.opsForValue().get(INVENTORY);
            if (Integer.parseInt(num) > 0) {
                log.info("当前库存为{}", num);
                redisTemplate.opsForValue().decrement(INVENTORY);
            } else {
                log.info("铁子，库存为0");
            }
        }
    }


    /**
     * redis 简单实现 可以实现锁的功能
     * <p>
     * 但是细思极恐，有好多毛病
     * ①未设置超时时间， 程序中断的时候 会一直占用锁
     * ②不支持等待，老一拿到锁，老二来了拿不到就哭着回去了，一点机会都不给老二留  无情
     */
    @Override
    public void redisTemplateTest() {
        /**
         * 加锁
         */
        if (redisTemplate.opsForValue().setIfAbsent(LOCKER_KEY, UUID.randomUUID().toString(), 1L, TimeUnit.MILLISECONDS)) {  //短期内释放锁
//        if (redisTemplate.opsForValue().setIfAbsent(LOCKER_KEY, UUID.randomUUID().toString())) {
            try {
                String num = redisTemplate.opsForValue().get(INVENTORY);
                if (Integer.parseInt(num) > 0) {
                    log.info("当前库存为{}", num);
                    redisTemplate.opsForValue().decrement(INVENTORY);
                } else {
                    log.info("没有了，铁子");
                }
            } finally {
                /**
                 * 释放
                 */
                redisTemplate.delete(LOCKER_KEY);
            }
        } else {
            log.info("人忒多，请稍候再试，铁子");
        }
    }


    /**
     * redisson  注意版本问题  springboot 的版本不同，引用的redisson的版本不同，不光是这个依赖，其他的依赖版本问题也很重要
     * <p>
     * 设置超时时间，但是没有等待时间
     */
    @Override
    public void redissonClientTest() {
        RLock lock = redissonClient.getLock(LOCKER_KEY);
        lock.lock();
        try {
            Thread.sleep(55000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

//        lock.lock(1L, TimeUnit.MILLISECONDS);
        String num = redisTemplate.opsForValue().get(INVENTORY);
        if (Integer.parseInt(num) > 0) {
            log.info("当前库存为{}", num);
            redisTemplate.opsForValue().decrement(INVENTORY);
        } else {
            log.info("没有了，铁子");
        }
        if (lock != null && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

    @Override
    public void redissonClientTryLockTest() {
        RLock lock = redissonClient.getLock(LOCKER_KEY);
        try {
            if (lock.tryLock(2L, 30L, TimeUnit.SECONDS)) {
                String num = redisTemplate.opsForValue().get(INVENTORY);
                if (Integer.parseInt(num) > 0) {
                    log.info("当前库存为{}", num);
                    redisTemplate.opsForValue().decrement(INVENTORY);
                } else {
                    log.info("没有了，铁子");
                }
            } else {
                log.info("人忒多，请稍候再试，铁子");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    private ScheduledFuture<?> scheduledFuture;

    @Override
    public void scheduler( String cron) {
        scheduledFuture = threadPoolTaskScheduler.schedule(new TaskRunnable("my schedule"), new CronTrigger(cron));
    }

    public void stop(   ) {
        scheduledFuture.cancel(true);
    }


}
